<?php

// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment -- Translator comment issues are tested in test case file 2.

__( 'string' ); // OK - no text domain known, so not checked.
__( 'string', 'something' ); // OK - no text domain known, so not checked.

// phpcs:set WordPress.WP.I18n text_domain[] my-slug

__( 'string' ); // Bad - no text domain passed.
__( 'string', 'something' ); // Bad - text domain mismatch.
__( 'string', "my-slug-too" ); // Bad - text domain mismatch.

__( "hell$varo", 'my-slug' ); // Bad, shouldn't use a string with variables.
__( "hell\$varo", 'my-slug' ); // OK, Variable is not interpolated.
__( "hell\\$varo", 'my-slug' ); // Bad, is interpolated.
__( "hell\\\$varo", 'my-slug' ); // OK, variable is escaped.

__( $var, 'my-slug' ); // Bad, shouldn't use variables.

__( 'string', SOMETHING ); // Bad, shouldn't use CONSTANTS.

__( 'string' . $var, 'my-slug' ); // Bad, shouldn't use variable for string.
__( $var . 'string', 'my-slug' ); // Bad, shouldn't use variable for string.

__( SOMETHING, 'my-slug' ); // Bad, shouldn't use constant for string.
__( 'string' . SOMETHING, 'my-slug' ); // Bad, shouldn't use constant for string.
__( SOMETHING . 'string', 'my-slug' ); // Bad, shouldn't use variable for string.

__( 'string', $domain ); // Bad, shouldn't use variable for domain.
__( 'string', 'my' . $domain ); // Bad, shouldn't use variable for domain.
__( 'string', $domain . 'my-slug' ); // Bad, shouldn't use variable for domain.

__( 'string', 'my-slug' ); // Good.
_x( 'string', 'context', 'my-slug' ); // Good.

_x( 'string', $var, 'my-slug' ); // Bad, shouldn't use variable for context.
_x( 'string', 'context' . $var, 'my-slug' ); // Bad, shouldn't use variable for context.
_x( 'string', $var . 'context', 'my-slug' ); // Bad, shouldn't use variable for context.

_x( 'string', SOMETHING, 'my-slug' ); // Bad, shouldn't use constant for context.
_x( 'string', SOMETHING . 'context', 'my-slug' ); // Bad, shouldn't use constant for context.
_x( 'string', 'context' . SOMETHING, 'my-slug' ); // Bad, shouldn't use constant for context.

_n( 'I have %d cat.', 'I have %d cats.', $number ); // Bad, no text domain.
_n( 'I have %d cat.', 'I have %d cats.', $number, 'my-slug' ); // OK.
_n( 'I have %d cat.', 'I have %d cats.', $number, "illegal $string" ); // Bad.
_n( 'I have %d cat.', 'I have %d cats.', $number, SOMETHING ); // Bad.

_n_noop( 'I have %d cat.', 'I have %d cats.' ); // Bad, no text domain.
_n_noop( 'I have %d cat.', 'I have %d cats.', 'my-slug' ); // OK.
_n_noop( 'I have %d cat.', 'I have %d cats.', "illegal $string" ); // Bad.
_n_noop( 'I have %d cat.', 'I have %d cats.',  SOMETHING ); // Bad.

_nx( 'I have %d cat.', 'I have %d cats.', $number, 'Not really.' ); // Bad, no text domain.
_nx( 'I have %d cat.', 'I have %d cats.', $number, $context ); // Bad, no text domain, variable context.
_nx( 'I have %d cat.', 'I have %d cats.', $number, 'Not really.', 'my-slug' ); // OK.
_nx( 'I have %d cat.', 'I have %d cats.', $number, $context, 'my-slug' ); // Bad.
_nx( 'I have %d cat.', 'I have %d cats.', $number, 'Not really.', "illegal $string" ); // Bad.
_nx( 'I have %d cat.', 'I have %d cats.', $number, 'Not really.', SOMETHING ); // Bad.

_nx_noop( 'I have %d cat.', 'I have %d cats.', 'Not really.' ); // Bad, no text domain.
_nx_noop( 'I have %d cat.', 'I have %d cats.', $context ); // Bad, no text domain, variable context.
_nx_noop( 'I have %d cat.', 'I have %d cats.', 'Not really.', 'my-slug' ); // OK.
_nx_noop( 'I have %d cat.', 'I have %d cats.', $context, 'my-slug' ); // Bad.
_nx_noop( 'I have %d cat.', 'I have %d cats.', 'Not really.', "illegal $string" ); // Bad.
_nx_noop( 'I have %d cat.', 'I have %d cats.', 'Not really.', SOMETHING ); // Bad.

translate( 'foo', 'my-slug' ); // Bad, low-level API function.
translate_with_gettext_context( 'foo', 'bar', 'my-slug' ); // Bad, low-level API function.

_( 'foo', 'my-slug' ); // Bad.

__( 'foo', 'my-slug', 'too-many-args' ); // Bad.
_x( 'string', 'context', 'my-slug', 'too-many-args' ); // Bad.
_n( 'I have %d cat.', 'I have %d cats.', $number, 'my-slug', 'too-many-args' ); // Bad.
_n_noop( 'I have %d cat.', 'I have %d cats.', 'my-slug', 'too-many-args' ); // Bad.
_nx_noop( 'I have %d cat.', 'I have %d cats.', 'Not really.', 'my-slug', 'too-many-args' ); // Bad.

// Make sure that multi-line string literals are accepted.
_nx( 'I have
%d cat.', 'I have
%d cats.', $number, 'Not
really.', 'my-slug' ); // OK.

// Ensure lack of spaces doesn't cause i18n error.
_n_noop('I have %d cat.', 'I have %d cats.', 'my-slug'); // OK.

// Dollar sign in literal string is not interpolated, so OK.
_n_noop( 'I have %d cat.', 'I have %d cats literal-string-so-$variable-not-interpolated.', 'my-slug' ); // OK.

// Multiple placeholders should have orderable placeholders.
__( 'There are %d monkeys in the %s', 'my-slug' ); // Multiple arguments should be numbered.
__( 'There are %1$d monkeys in the %2$s', 'my-slug' ); // OK.
_n( 'There is %d monkey in the %s', 'There are %d monkeys in the %s', $number, 'my-slug' ); // Multiple arguments should be numbered.
_n( 'There is %1$d monkey in the %2$s', 'In the %2$s there are %1$d monkeys', $number, 'my-slug' ); // OK.

// The singular form should use placeholders if the plural does.
// https://codex.wordpress.org/I18n_for_WordPress_Developers#Plurals
_n( 'I have a cat.', 'I have %d cats.', $number, 'my-slug' ); // Bad, singular should have placeholder.
_n_noop( 'I have a cat.', 'I have %d cats.', 'my-slug' ); // Bad, singular should have placeholder.
_nx( 'I have a cat.', 'I have %d cats.', $number, 'Not really.', 'my-slug' ); // Bad, singular should have placeholder.
_nx_noop( 'I have a cat.', 'I have %d cats.', 'Not really.', 'my-slug' ); // Bad, singular should have placeholder.

__( '%s', 'my-slug' ); // Bad, don't waste translator's time.
__( '%1$s%2$s', 'my-slug' ); // Bad, don't waste translator's time.
_n( 'I have %d cat.', '%d', $number, 'my-slug' ); // Bad, move the logic out of the translation.
__( '\'%s\'', 'my-slug' ); // OK (ish. this is a technical test, not a great string).

// Issue #681.
__( 'String with a literal %%', 'my-slug'); // Ok, replacement would be a single %.
__( 'String with a literal %% and a %s placeholder', 'my-slug'); // Ok.
__( 'A replacement variable can not start with "%%cf_" or "%%ct_" as these are reserved for the WPSEO standard variable variables for custom fields and custom taxonomies. Try making your variable name unique.', 'my-slug' ); // Ok.

// Issue #698 - fix for auto-fixing placeholder ordering.
__( '%s - %s - %s - %s', 'my-slug' ); // Bad - Multiple of the same placeholders.
__( '%d - %d - %d - %d', 'my-slug' ); // Bad - Multiple of the same placeholders.
__( '%% - %b - %c - %d - %e - %E - %f - %F - %g - %G - %o - %s - %u - %x - %X', 'my-slug' ); // Bad - All simple variations.
__( "%d for %d 'item'", 'my-slug' ); // Bad - Multiple of the same placeholders in a double quoted string.
__( '%04d for %02d item', 'my-slug' ); // Bad - Placeholder with other specifier, but no position.
__( "%04d for %'.9d item", 'my-slug' ); // Bad - Placeholder with other specifier, but no position, double quoted string.

// Related to issue #698 - mixed ordered and non-ordered placeholders.
__( '%1$d for %d item', 'my-slug' ); // Bad.
__( '%1$d for %d and %d item', 'my-slug' ); // Bad.

// Nowdoc syntax.
__( <<<'EOD'
%1$d for %d item
EOD
, 'my-slug' ); // Bad.

// Multi-line heredoc syntax.
_nx( <<<'EOD'
I have
%d cat and %d
dog.
EOD
, <<<'EOD'
I have
%d cats and %d
dogs.
EOD
, $number, <<<'EOD'
Not
really.
EOD
, 'my-slug' ); // OK.

// phpcs:set WordPress.WP.I18n text_domain[] my-slug,default
__( "String default text domain.", "my-slug" ); // Ok.
__( "String default text domain.", "default" ); // Ok.
__( "String default text domain.", 'something' ); // Bad, text domain mismatch.
__( 'String default text domain.' ); // Warning, use default.

// phpcs:set WordPress.WP.I18n text_domain[] default
__( 'String default text domain.', 'my-slug' ); // Bad because text_domain is only 'default'.
__( 'String default text domain.', 'default' ); // Warning, text domain can be omitted.
__( 'String default text domain.', /* Explicitly set for reason. */ 'default' ); // Warning, text domain can be omitted (not auto-fixable).
__( 'String default text domain.' ); // Ok because default domain is 'default' and it matches one of the supplied configured text domains.

// Issue #1266.
// phpcs:set WordPress.WP.I18n text_domain[] my-slug
$mo->translate( $string ); // OK, not a function, but a method call.
Something\esc_html_e( $string ); // OK, not the WP function, but namespaced function call.

// Issue #719.
class Foo {
	public function translate( $key ) {
		return '';
	}
}

// Issue 1355.
$offset_or_tz = _x( '0', 'default GMT offset or timezone string', 'my-slug' );

$test = __( '%1$s %2$s', 'my-slug' ); // OK(ish), placeholder order may change depending on language.
$test = __( '   %s    ', 'my-slug' ); // Bad, no translatable content.

// Missing plural argument.
_n_noop($singular); // Bad x 3.

// This test is needed to verify that the missing plural argument above does not cause an internal error, stopping the run.
_n_noop( 'I have %d cat.', 'I have %d cats.' ); // Bad.

// HTML wrapped strings.
__( '<address>123 Fake Street</address>', 'my-slug' ); // Bad, string shouldn't be wrapped in HTML.
__( 'I live at <address>123 Fake Street</address>', 'my-slug' ); // Okay, no consensus on HTML tags within strings.
__( '<address>123 Fake Street</address> is my home', 'my-slug' ); // Okay, no consensus on HTML tags within strings.
__( '<span>Text</span> More text <span>Text</span>', 'my-slug' ); // Good, we're not wrapping
__( '<div class="my-class">Translatable content</div>', 'my-slug' ); // Bad
__( '<option id="%1$s" value="%2$s">Translatable option name</option>', 'my-slug' ); // Wrapping is okay, since there are placeholders
__( '<b><i>Foo</i></b>', 'my-slug' ); // Bad
__( '<foo>Foo</foo>', 'my-slug' ); // Good

// Strings wrapped in `<a href="...">...</a>` aren't flagged, since the link target might require localization.
__( '<a href="https://wordpress.org">WordPress</a>', 'my-slug' ); // Good
__( '<a id="anchor">translatable text</a>', 'my-slug' ); // Bad, since no href
__( '<a>translatable text</a>', 'my-slug' ); // Bad, since no href
__( '<a href="%s">translatable text</a>', 'my-slug' ); // Good

// Strings containing malformed XML, to make sure we're able to parse them. No warnings should be thrown.
__( '<div>text to translate', 'my-slug' );
__( 'text to <div>translate', 'my-slug' );
__( 'text < to translate', 'my-slug' );
__( 'text to > translate', 'my-slug' );
__( '<div data-type=">foo">my address</div>', 'my-slug' );
__( '<div>text to translate<div>', 'my-slug' );
__( '<>text to translate</>', 'my-slug' );
__( '<>text to translate<>', 'my-slug' );
__( '<div><div>', 'my-slug' );
__( '<<>>text to translate<<>', 'my-slug' );
__( '<<>>123</<>', 'my-slug' );
__( '<b><i>Foo</b></i>', 'my-slug' );
__( '<b><i>Foo</b></i></b>', 'my-slug' );
__( '<foo>Foo</bar>', 'my-slug' );

// Test handling of more complex embedded variables and expressions. All of these should throw an error.
__( "${foo}", 'my-slug' );
__( "{$foo['bar']}", 'my-slug' );
__( "{$foo->bar()}", 'my-slug' );
__( "{$foo['bar']->baz()()}", 'my-slug' );
__( "${foo->bar}", 'my-slug' );
__( "${foo["${bar['baz']}"]}", 'my-slug' );

// Safeguard defensive coding.
__( /* comment */, 'my-slug' ); // Bad, missing first param (parse error).

// phpcs:set WordPress.WP.I18n text_domain[] default

// Safeguard that a comment after a "default" text domain is not removed.
__( 'String default text domain.', 'default' /*comment*/ ); // Warning, text domain can be omitted.

// Test mismatched placeholders.
_n_noop( 'I have %1$d cat and %2$d dog.' ); // Bad, missing $plural argument, ignore for single/plural placeholder check.
_n_noop( 'I have %1$d cat and %2$d dog.', $plural ); // Bad, non singular string $plural argument, ignore for single/plural placeholder check.
_n_noop( 'I have %1$d cat and %2$d dog.', $plural . $text ); // Bad, non singular string $plural argument, ignore for single/plural placeholder check.

_n_noop( 'I have %1$d cat and %2$d dog.', 'I have %1$d cats and %2$d dog.' ); // Ok.
_n_noop( 'I have %2$d cat and %1$d dog.', 'I have %1$d cats and %2$d dog.' ); // Ok, or at least not something we flag.
_n_noop( 'I have %1$d cat and %3$d dog.', 'I have %1$d cats and %2$d dog.' ); // Bad, different placeholders used in single vs plural text.
_n_noop( 'I have %d cat, %d dog and %d canaries.', 'I have %d cats, %d dog and canaries.' ); // Bad, mismatched placeholders count.
_n_noop( 'I have %1$d cat, %2$d dog and %3$d canaries.', 'I have %1$d cats, %2$d dog and canaries.' ); // Bad, mismatched numbered placeholders count.
_n_noop( 'I have %1$d cat, %2$d dog and %2$d canaries.', 'I have %1$d cats, %2$d dog and canaries.' ); // Bad, mismatched numbered placeholders count.

/*
 * Safeguard support for PHP 8.0+ named parameters.
 * Each of these function calls uses named parameters with an unconventional param order to test this properly.
 */
// phpcs:set WordPress.WP.I18n text_domain[] my-slug
translate( domain: 'my-slug', text: 'translate me', extra: 'default' ); // Bad: too many arguments + use of translate().
__( domain: 'my-slug' ); // Bad: missing $text argument.
__( single: 'translate me', domain: 'my-slug' ); // Bad: missing $text argument (invalid 'single' named arg).
esc_attr__(
	domain: 'my-slug',
	text: 'translate
me multi line', // OK, multi-line text string.
);
esc_html__(
	domain: PLUGIN_TEXTDOMAIN, // Bad: not single text string literal.
	text: 'translate ' . 'me', // Bad: not single text string literal.
);
_e(
	domain: 'my-slug',
	text: $translateMe, // Bad: not single text string literal.
);
esc_attr_e(
	domain: 'my-slug',
	text: "translate
$me multi line", // Bad: interpolated variable found.
);
esc_html_e(
	domain: 'different-slug', // Bad: wrong text domain.
	text: 'translate me',
);
esc_html_x(
	context: 'context',
	domain: 'my-slug',
	text: 'trans %s late %1$s me', // Bad: mixing ordered and non-ordered placeholders.
);
_x( context: 'context', text: 'translate %s me %s', domain:  'my-slug' ); // Bad: multiple placeholders, but no ordering.
_ex( domain: 'my-slug', text: '%s', context: 'context', ); // Bad: no translatable content.
esc_attr_x( domain: 'my-slug', context: 'context', text: '<div>translate me</div>', ); // Bad: wrapped in HTML.
_n( number: $i, domain: 'my-slug', single: 'translate me', plural: 'translate %s me' ); // Bad: missing singular placeholder.
_nx( number: $i, domain: 'my-slug', plural: 'translate %s me', single: 'translate %1$s me', context: 'context', ); // Bad: mismatch between single/plural placeholders.

// Safeguard that the SuperfluousDefaultTextDomain fixer handles trailing commas correctly.
// Note: if the original code contained a trailing comma, it is fine for the fixed code to also contain a trailing comma.
// phpcs:set WordPress.WP.I18n text_domain[] default
__( 'translate me', 'default', ); // Bad: superfluous domain (positional with trailing comma).
__( 'translate me', 'default' /*comment*/, ); // Bad: superfluous domain (positional with trailing comma and comment).

// Safeguard handling of named params by the SuperfluousDefaultTextDomain fixer.
__( text: 'translate me', domain: 'default' ); // Bad: superfluous domain.
__( text: 'translate me', domain: 'default', ); // Bad: superfluous domain (with trailing comma).
_n_noop( domain: 'default', singular: 'translate me', plural: 'translate me', ); // Bad: superfluous domain.
_nx_noop( singular: 'translate me', domain: 'default' /*comment*/, plural: 'translate me', context: 'context', ); // Bad: superfluous domain.

// These should not be auto-fixable as we don't want to remove the comment.
__( text: 'translate me', domain: /*comment*/ 'default' ); // Bad: superfluous domain.
_n_noop( domain: 'default' /*comment*/, singular: 'translate me', plural: 'translate me', ); // Bad: superfluous domain.
_nx_noop( singular: 'translate me', domain /*comment*/: 'default', plural: 'translate me', context: 'context', ); // Bad: superfluous domain.

// Safeguard bug fix: replacing placeholders in multi-line text strings.
_nx( 'I have
%d cat and %d dog.', 'I have
%d cats and %d dogs.', $number, 'Not
really.' ); // Bad, multiple arguments should be numbered.

// Show an error when the text domain is an empty string.
esc_html_e( 'foo', '' ); // Bad: text domain mismatch.

// Issue #1948 - Show an error when the text domain is an empty string and no text domain has been passed.
// phpcs:set WordPress.WP.I18n text_domain[]
esc_html_e( 'foo', '' ); // Bad: text-domain can not be empty.

// PHP 8.0+: safeguard handling of newly introduced placeholders.
__( 'There are %1$h monkeys in the %H', 'my-slug' ); // Bad: multiple arguments should be numbered.

// phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
